package cn.com.lyric.lrc;

import java.util.List;

import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.Scroller;

/***
 * 须知：
 * 在ViewGroup里面 scrollTo，scrollBy方法移动的是子View
 * 在View里面scrollTo，scrollBy方法移动的是View里面绘制的内容
 * 要点：
 * 1:歌词的上下平移用什么实现？
 * 用Scroller实现，Scroller只是一个工具而已，
 * 真正实现滚动效果的还是View的scrollTo方法
 * 2：歌词的水平滚动怎么实现？
 * 通过属性动画ValueAnimator控制高亮歌词绘制的x轴起始坐标
 *
 * @author luwenlong  2016/3/10
 */
public class LrcView extends View implements ILrcView {

    private static final String DEFAULT_COLOR_FOR_HIGHT_LIGHT_LRC = "#019858";//高亮歌词的默认字体颜色
    private static final String DEFAULT_COLOR_FOR_OTHER_LRC = "#FFFFFF";//其他歌词的默认字体颜色
    private static final String COLOR_FOR_TIME_LINE = "#EA0000";//时间线的颜色

    private static final float DEFAULT_SIZE_FOR_HIGHT_LIGHT_LRC = 60;//高亮歌词的默认字体大小
    private static final float DEFAULT_SIZE_FOR_OTHER_LRC = 50;//其他歌词的默认字体大小
    private static final int SIZE_FOR_TIME = 18;//时间文字大小
    private static final String DEFAULT_TEXT = "酷狗音乐，传播好音乐";//无歌词数据的时候 显示的默认文字
    private static final float DEFAULT_PADDING = 20;//歌词间默认的行距

    private static final int DURATION_FOR_LRC_SCROLL = 1500;//移动一句歌词的持续时间
    private static final int DURATION_FOR_ACTION_UP = 400;//停止触摸时 如果View需要滚动 时的持续时间

    private Scroller scroller;
    private Paint paintForHighLightLrc;//高亮歌词
    private Paint paintForOtherLrc;//画其他歌词的画笔

    private Paint paintForTimeLine;//画时间线的画笔
    private boolean mIsDrawTimeLine = false;//是否画时间线

    private List<LrcRow> lrcRows;//所有的歌词
    private int curRow = -1;//当前高亮歌词的行号
    private int lastRow = -1;//上一次的高亮歌词的行号

    private float curFraction = 0;//控制文字缩放的因子
    private int touchSlop;

    private float curTextXForHighLightLrc;//高亮歌词当前的其实x轴绘制坐标

    private ValueAnimator animator;//控制歌词水平滚动的属性动画

    public LrcView(Context context) {
        super(context);
        init();
    }

    public LrcView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }


    /**
     * 初始化画笔等
     */
    @Override
    public void init() {
        scroller = new Scroller(getContext());
        paintForHighLightLrc = new Paint();
        paintForHighLightLrc.setColor(Color.parseColor(DEFAULT_COLOR_FOR_HIGHT_LIGHT_LRC));
        paintForHighLightLrc.setTextSize(DEFAULT_SIZE_FOR_HIGHT_LIGHT_LRC);

        paintForOtherLrc = new Paint();
        paintForOtherLrc.setColor(Color.parseColor(DEFAULT_COLOR_FOR_OTHER_LRC));
        paintForOtherLrc.setTextSize(DEFAULT_SIZE_FOR_OTHER_LRC);

        touchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
    }

    private int totleDrawRow;

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (lrcRows == null || lrcRows.size() == 0) {
            //画默认的显示文字
            float textWidth = paintForOtherLrc.measureText(DEFAULT_TEXT);
            float textX = (getWidth() - textWidth) / 2;
            canvas.drawText(DEFAULT_TEXT, textX, getHeight() / 2, paintForOtherLrc);
            return;
        }
        if (totleDrawRow == 0) {
            //初始化将要绘制的歌词行数
            totleDrawRow = (int) (getHeight() / (DEFAULT_SIZE_FOR_OTHER_LRC + DEFAULT_PADDING)) +14;
        }
        //因为不需要将所有歌词画出来
        int minRaw = curRow - (totleDrawRow - 1) / 2;
        int maxRaw = curRow + (totleDrawRow - 1) / 2;
        minRaw = Math.max(minRaw, 0); //处理上边界
        maxRaw = Math.min(maxRaw, lrcRows.size() - 1); //处理下边界
        //实现渐变的最大歌词行数
        int count = Math.max(maxRaw - curRow, curRow - minRaw);
        //两行歌词间字体颜色变化的透明度
        int alpha = (0xFF - 0x11) / count;
        //画出来的第一行歌词的y坐标
        float rowY = getHeight() / 2 + minRaw * (DEFAULT_SIZE_FOR_OTHER_LRC + DEFAULT_PADDING);
        for (int i = minRaw; i <= maxRaw; i++) {

            if (i == curRow) {//画高亮歌词
                //因为有缩放效果，所有需要动态设置歌词的字体大小
                float textSize = DEFAULT_SIZE_FOR_OTHER_LRC + (DEFAULT_SIZE_FOR_HIGHT_LIGHT_LRC - DEFAULT_SIZE_FOR_OTHER_LRC) * curFraction;
                paintForHighLightLrc.setTextSize(textSize);

                String text = lrcRows.get(i).getContent();//获取到高亮歌词
                float textWidth = paintForHighLightLrc.measureText(text);//用画笔测量歌词的宽度
                if (textWidth > getWidth()) {
                    //如果歌词宽度大于view的宽，则需要动态设置歌词的起始x坐标，以实现水平滚动
                    canvas.drawText(text, curTextXForHighLightLrc, rowY, paintForHighLightLrc);
                } else {
                    //如果歌词宽度小于view的宽，则让歌词居中显示
                    float textX = (getWidth() - textWidth) / 2;
                    canvas.drawText(text, textX, rowY, paintForHighLightLrc);
                }
            } else {
                if (i == lastRow) {//画高亮歌词的上一句
                    //因为有缩放效果，所有需要动态设置歌词的字体大小
                    float textSize = DEFAULT_SIZE_FOR_HIGHT_LIGHT_LRC - (DEFAULT_SIZE_FOR_HIGHT_LIGHT_LRC - DEFAULT_SIZE_FOR_OTHER_LRC) * curFraction;
                    paintForOtherLrc.setTextSize(textSize);
                } else {//画其他的歌词
                    paintForOtherLrc.setTextSize(DEFAULT_SIZE_FOR_OTHER_LRC);
                }
                String text = lrcRows.get(i).getContent();
                float textWidth = paintForOtherLrc.measureText(text);
                float textX = (getWidth() - textWidth) / 2;
                //如果计算出的textX为负数，将textX置为0(实现：如果歌词宽大于view宽，则居左显示，否则居中显示)
                textX = Math.max(textX, 0);
                //实现颜色渐变  从0xFFFFFFFF 逐渐变为 0x11FFFFFF(颜色还是白色，只是透明度变化)
                int curAlpha = 255 - (Math.abs(i - curRow) - 1) * alpha; //求出当前歌词颜色的透明度
                paintForOtherLrc.setColor(0x1000000 * curAlpha + 0xffffff);
                canvas.drawText(text, textX, rowY, paintForOtherLrc);
            }
            //计算出下一行歌词绘制的y坐标
            rowY += DEFAULT_SIZE_FOR_OTHER_LRC + DEFAULT_PADDING;
        }

        //画时间线和时间
        if (mIsDrawTimeLine && null != paintForTimeLine) {
            float y = getHeight() / 2 + getScrollY();
            canvas.drawText(lrcRows.get(curRow).getTimeStr(), 0, y - 5, paintForTimeLine);
            canvas.drawLine(0, y, getWidth(), y, paintForTimeLine);
        }

    }

    /**
     * 是否可拖动歌词
     **/
    private boolean canDrag = false;
    /**
     * 事件的第一次的y坐标
     **/
    private float firstY;
    /**
     * 事件的上一次的y坐标
     **/
    private float lastY;
    private float lastX;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (lrcRows == null || lrcRows.size() == 0) {
            return false;
        }
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                firstY = event.getRawY();
                lastX = event.getRawX();
                break;
            case MotionEvent.ACTION_MOVE:
                if (!canDrag) {
                    if (Math.abs(event.getRawY() - firstY) > touchSlop && Math.abs(event.getRawY() - firstY) > Math.abs(event.getRawX() - lastX)) {
                        canDrag = true;
                        mIsDrawTimeLine = true;
                        scroller.forceFinished(true);
                        stopScrollLrc();
                        curFraction = 1;
                    }
                    lastY = event.getRawY();
                }
                if (canDrag) {
                    float offset = event.getRawY() - lastY;//偏移量
                    if (getScrollY() - offset < 0) {
                        if (offset > 0) {
                            offset = offset / 3;
                        }
                    } else if (getScrollY() - offset > lrcRows.size() * (DEFAULT_SIZE_FOR_OTHER_LRC + DEFAULT_PADDING) - DEFAULT_PADDING) {
                        if (offset < 0) {
                            offset = offset / 3;
                        }
                    }
                    scrollBy(getScrollX(), -(int) offset);
                    lastY = event.getRawY();
                    int currentRow = (int) (getScrollY() / (DEFAULT_SIZE_FOR_OTHER_LRC + DEFAULT_PADDING));
                    currentRow = Math.min(currentRow, lrcRows.size() - 1);
                    currentRow = Math.max(currentRow, 0);
                   // seekTo(lrcRows.get(currentRow).getTime(), false, false);
                    return true;
                }
                lastY = event.getRawY();
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (!canDrag) {
                    if (onLrcClickListener != null) {
                        onLrcClickListener.onClick();
                    }
                } else {
                    if (onSeekToListener != null && curRow != -1) {
                        onSeekToListener.onSeekTo(lrcRows.get(curRow).getTime());
                    }
                    if (getScrollY() < 0) {
                        smoothScrollTo(0, DURATION_FOR_ACTION_UP);
                    } else if (getScrollY() > lrcRows.size() * (DEFAULT_SIZE_FOR_OTHER_LRC + DEFAULT_PADDING) - DEFAULT_PADDING) {
                        smoothScrollTo((int) (lrcRows.size() * (DEFAULT_SIZE_FOR_OTHER_LRC + DEFAULT_PADDING) - DEFAULT_PADDING), DURATION_FOR_ACTION_UP);
                    }

                    canDrag = false;
                    mIsDrawTimeLine = false;
                    invalidate();
                }
                break;
        }
        return true;
    }

    /**
     * 为LrcView设置歌词List集合数据
     */
    @Override
    public void setLrcRows(List<LrcRow> lrcRows) {
        reset();
        this.lrcRows = lrcRows;
        invalidate();
    }

    @Override
    public void seekTo(int progress, boolean fromSeekBar, boolean fromSeekBarByUser) {
        if (lrcRows == null || lrcRows.size() == 0) {
            return;
        }
        //如果是由seekbar的进度改变触发 并且这时候处于拖动状态，则返回
        if (fromSeekBar && canDrag) {
            return;
        }
        for (int i = lrcRows.size() - 1; i >= 0; i--) {
            if (progress >= lrcRows.get(i).getTime()) {
                if (curRow != i) {
                    lastRow = curRow;
                    curRow = i;
                    if (fromSeekBarByUser) {
                        if (!scroller.isFinished()) {
                            scroller.forceFinished(true);
                        }
                        scrollTo(getScrollX(), (int) (curRow * (DEFAULT_SIZE_FOR_OTHER_LRC + DEFAULT_PADDING)));
                    } else {
                        smoothScrollTo((int) (curRow * (DEFAULT_SIZE_FOR_OTHER_LRC + DEFAULT_PADDING)), DURATION_FOR_LRC_SCROLL);
                    }
                    //如果高亮歌词的宽度大于View的宽，就需要开启属性动画，让它水平滚动
                    float textWidth = paintForHighLightLrc.measureText(lrcRows.get(curRow).getContent());
                    if (textWidth > getWidth()) {
                        if (fromSeekBarByUser) {
                            scroller.forceFinished(true);
                        }
                        startScrollLrc(getWidth() - textWidth, (long) (lrcRows.get(curRow).getTotalTime() * 0.6));
                    }
                    invalidate();
                }
                break;
            }
        }

    }

    /**
     * 开始水平滚动歌词
     *
     * @param endX     歌词第一个字的最终的x坐标
     * @param duration 滚动的持续时间
     */
    private void startScrollLrc(float endX, long duration) {
        if (animator == null) {
            animator = ValueAnimator.ofFloat(0, endX);
            animator.addUpdateListener(updateListener);
        } else {
            curTextXForHighLightLrc = 0;
            animator.cancel();
            animator.setFloatValues(0, endX);
        }
        animator.setDuration(duration);
        animator.setStartDelay((long) (duration * 0.3)); //延迟执行属性动画
        animator.start();
    }

    /**
     * 停止歌词的滚动
     */
    private void stopScrollLrc() {
        if (animator != null) {
            animator.cancel();
        }
        curTextXForHighLightLrc = 0;
    }

    /***
     * 监听属性动画的数值值的改变
     */
    AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {

        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            curTextXForHighLightLrc = (Float) animation.getAnimatedValue();
            invalidate();
        }
    };

    /**
     * 设置歌词的缩放比例
     */
    @Override
    public void setLrcScalingFactor(float scalingFactor) {
    }

    /**
     * 重置
     */
    @Override
    public void reset() {
        if (!scroller.isFinished()) {
            scroller.forceFinished(true);
        }
        lrcRows = null;
        scrollTo(getScrollX(), 0);
        invalidate();
    }

    @Override
    public void showPaintForTimeLine() {
        paintForTimeLine = new Paint();
        paintForTimeLine.setColor(Color.parseColor(COLOR_FOR_TIME_LINE));
        paintForTimeLine.setTextSize(SIZE_FOR_TIME);

    }


    /**
     * 平滑的移动到某处
     *
     * @param dstY
     */
    private void smoothScrollTo(int dstY, int duration) {
        int oldScrollY = getScrollY();
        int offset = dstY - oldScrollY;
        scroller.startScroll(getScrollX(), oldScrollY, getScrollX(), offset, duration);
        invalidate();
    }

    @Override
    public void computeScroll() {
        if (!scroller.isFinished()) {
            if (scroller.computeScrollOffset()) {
                int oldY = getScrollY();
                int y = scroller.getCurrY();
                if (oldY != y && !canDrag) {
                    scrollTo(getScrollX(), y);
                }
                curFraction = scroller.timePassed() * 3f / DURATION_FOR_LRC_SCROLL;
                curFraction = Math.min(curFraction, 1F);
                invalidate();
            }
        }
    }

    private OnSeekToListener onSeekToListener;

    public void setOnSeekToListener(OnSeekToListener onSeekToListener) {
        this.onSeekToListener = onSeekToListener;
    }

    public interface OnSeekToListener {
        void onSeekTo(int progress);
    }

    private OnLrcClickListener onLrcClickListener;

    public void setOnLrcClickListener(OnLrcClickListener onLrcClickListener) {
        this.onLrcClickListener = onLrcClickListener;
    }

    public interface OnLrcClickListener {
        void onClick();
    }

}
